﻿using System;
using CalcSharp.Core;
using CalcSharp.Core.Containers;
using CalcSharp.MDIUI;
using dnAnalytics.Math;
using System.Collections.Generic;
using CalcSharp.MDIUI.ToolBars;
using CalcSharp.Core.General;

namespace CalcSharp
{
    enum Inputtype : byte
    {
        complex,
        matrix,
        set
    }

    enum storeoption : byte
    {
        copy,
        add,
        sub,
        mul,
        div
    }

    class memsegment
    {
        public int i_X;
        public int i_Y;
        public string name;
        public memsegment(int x, int y, string n)
        {
            this.i_X = x;
            this.i_Y = y;
            this.name = n;
        }
    }

    sealed class mem_man
    {
        private mem_man() { }
        private static bool isFormatOf(Inputtype it, string s)
        {
            bool result = false;
            string[] spbuff;
            switch (it)
            {
                case Inputtype.complex:
                    spbuff = s.Split(';');
                    if (spbuff.Length == 2) result = true;
                    break;
                case Inputtype.matrix:
                    spbuff = s.Split('}');
                    if (spbuff.Length > 1)
                    {
                        if (spbuff[0].Contains("{")) result = true;
                    }
                    break;
                case Inputtype.set:
                    if ((s.IndexOf('{') == 0) && (s.Contains("}"))) result = true;
                    break;
            }
            return result;
        }

        private static memsegment Baseformatof(Inputtype it, string s)
        {
            memsegment result = new memsegment(-1, -1, null);
            string[] spbuff;
            string[] spbuff2;
            switch (it)
            {
                case Inputtype.set:
                    if (Patterns.Match(s, Patterns.FloatArrayIndexer))
                    {
                        spbuff = s.Split('[');
                        result = new memsegment(Convert.ToInt32(MiscFuncts.parsestr(spbuff[1].Replace("]", ""))) - 1, -1, spbuff[0]);
                    }
                    break;
                case Inputtype.matrix:
                    if (Patterns.Match(s, Patterns.MatrixIndexer))
                    {
                        spbuff = s.Split('[');
                        spbuff2 = spbuff[1].Split(';');
                        if (spbuff2.Length == 2) result = new memsegment(Convert.ToInt32(MiscFuncts.parsestr(spbuff2[0].Replace("[", ""))) - 1, Convert.ToInt32(MiscFuncts.parsestr(spbuff2[1].Replace("]", ""))) - 1, spbuff[0]);
                    }
                    break;
            }
            return result;
        }

        public static void StoreTres(Tresult re, string dest, storeoption st)
        {
            memsegment r = new memsegment(-1, -1, null);
            #region Float
            if (!double.IsNaN(re.floatingpt))
            {
                r = Baseformatof(Inputtype.matrix, dest);
                if (r.i_X != -1 && r.i_Y != -1 && Program.loaded.Mtrx_mem.isset(r.name))
                {
                    switch (st)
                    {
                        case storeoption.copy:
                            Program.loaded.Mtrx_mem[r.name][r.i_X, r.i_Y] = re.floatingpt;
                            break;
                        case storeoption.add:
                            Program.loaded.Mtrx_mem[r.name][r.i_X, r.i_Y] += re.floatingpt;
                            break;
                        case storeoption.sub:
                            Program.loaded.Mtrx_mem[r.name][r.i_X, r.i_Y] -= re.floatingpt;
                            break;
                        case storeoption.mul:
                            Program.loaded.Mtrx_mem[r.name][r.i_X, r.i_Y] *= re.floatingpt;
                            break;
                        case storeoption.div:
                            Program.loaded.Mtrx_mem[r.name][r.i_X, r.i_Y] /= re.floatingpt;
                            break;
                    }
                }
                else
                {
                    r = Baseformatof(Inputtype.set, dest);
                    if (r.i_X != -1 && Program.loaded.Set_mem.isset(r.name))
                    {
                        switch (st)
                        {
                            case storeoption.copy:
                                Program.loaded.Set_mem[r.name][r.i_X] = re.floatingpt;
                                break;
                            case storeoption.add:
                                Program.loaded.Set_mem[r.name][r.i_X] -= re.floatingpt;
                                break;
                            case storeoption.sub:
                                Program.loaded.Set_mem[r.name][r.i_X] += re.floatingpt;
                                break;
                            case storeoption.mul:
                                Program.loaded.Set_mem[r.name][r.i_X] *= re.floatingpt;
                                break;
                            case storeoption.div:
                                Program.loaded.Set_mem[r.name][r.i_X] /= re.floatingpt;
                                break;
                        }
                    }
                    else
                    {
                        switch (st)
                        {
                            case storeoption.copy:
                                Program.loaded.Mem[dest] = re.floatingpt;
                                break;
                            case storeoption.add:
                                Program.loaded.Mem[dest] += re.floatingpt;
                                break;
                            case storeoption.sub:
                                Program.loaded.Mem[dest] -= re.floatingpt;
                                break;
                            case storeoption.mul:
                                Program.loaded.Mem[dest] *= re.floatingpt;
                                break;
                            case storeoption.div:
                                Program.loaded.Mem[dest] /= re.floatingpt;
                                break;
                        }
                        ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mFloat);
                    }
                }
            }
            #endregion
            #region Matrix
            else if (re.matrixtype != null && st == storeoption.copy)
            {
                Program.loaded.Mtrx_mem[dest] = re.matrixtype;
                ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mMatrix);
            }
            #endregion
            #region Complex
            else if (!Complex.IsNaN(re.complex))
            {
                switch (st)
                {
                    case storeoption.copy:
                        Program.loaded.CPLX_mem[dest] = re.complex;
                        break;
                    case storeoption.add:
                        Program.loaded.CPLX_mem[dest] += re.complex;
                        break;
                    case storeoption.sub:
                        Program.loaded.CPLX_mem[dest] -= re.complex;
                        break;
                    case storeoption.mul:
                        Program.loaded.CPLX_mem[dest] *= re.complex;
                        break;
                    case storeoption.div:
                        Program.loaded.CPLX_mem[dest] /= re.complex;
                        break;
                }
                ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mCplx);
            }
            #endregion
            #region Set
            else if (!re.settype.isNull() && st == storeoption.copy)
            {
                Program.loaded.Set_mem[dest] = re.settype;
                ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mSet);
            }
            #endregion
            #region String
            else if (!string.IsNullOrEmpty(re.stringtype) && st == storeoption.copy)
            {
                Program.loaded.String_mem[dest] = re.stringtype;
                ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mString);
            }
            #endregion
            else throw new CSException("Unsuported operation by this type");

        }

        private static double[] GetMtrxSize(string input)
        {
            string[] cords = input.Split(';');
            double[] ret = new double[2];
            if (cords.Length > 1)
            {
                ret[0] = MiscFuncts.parsestr(cords[0]);
                ret[1] = MiscFuncts.parsestr(cords[1]);
                return ret;
            }
            else return null;
        }

        public static void MOV(string dest, string src)
        {
            byte op;
            switch (dest[0])
            {
                case '$':
                    if (dest.EndsWith("[]")) op = 3; //set
                    else op = 0; //variable
                    break;
                case '#':
                    op = 1; //matrix
                    break;
                case '&':
                    op = 2; //complex
                    break;
                case '~':
                    op = 4; //string
                    break;
                default:
                    throw new CSException("Invalid destination memory");
            }
            memsegment r = new memsegment(-1, -1, null);
            ReversePolishNotation rpn = new ReversePolishNotation();

            switch (op)
            {
                case 0: //variable
                    //copy ->
                    if (Program.loaded.Mem.isset(src)) Program.loaded.Mem[dest] = Program.loaded.Mem[src];
                    //create ->
                    else
                    {
                        rpn.Parse(src);
                        Program.loaded.Mem[dest] = rpn.Evaluate();
                    }
                    ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mFloat);
                    break;
                case 1: //matrix
                    r = mem_man.Baseformatof(Inputtype.matrix, dest);
                    //copy ->
                    if (Program.loaded.Mtrx_mem.isset(src)) Program.loaded.Mtrx_mem[dest] = Program.loaded.Mtrx_mem[src];
                    //alter single element ->
                    else if (r.i_X != -1 && r.i_Y != -1 && Program.loaded.Mtrx_mem.isset(r.name)) Program.loaded.Mtrx_mem[r.name][r.i_X, r.i_Y] = MiscFuncts.parsestr(src);
                    //create ->
                    else if (mem_man.isFormatOf(Inputtype.matrix, src)) Program.loaded.Mtrx_mem[dest] = MatrixUI.makematrix(src);
                    else
                    {
                        double[] cords = GetMtrxSize(src);
                        Program.loaded.Mtrx_mem[dest] = new DoubleMatrix(Convert.ToInt32(cords[0]), Convert.ToInt32(cords[1]), 0);
                    }
                    ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mMatrix);
                    break;
                case 2: //complex
                    //copy ->
                    if (Program.loaded.CPLX_mem.isset(src)) Program.loaded.CPLX_mem[dest] = Program.loaded.CPLX_mem[src];
                    //create ->
                    else if (mem_man.isFormatOf(Inputtype.complex, src)) Program.loaded.CPLX_mem[dest] = CPLXMath.ParseComplex(src);
                    ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mCplx);
                    break;
                case 3: //set
                    r = mem_man.Baseformatof(Inputtype.set, dest);
                    // copy ->
                    if (Program.loaded.Set_mem.isset(src)) Program.loaded.Set_mem[dest] = Program.loaded.Set_mem[src];
                    //alter single element ->
                    else if (r.i_X != -1 && Program.loaded.Set_mem.isset(r.name)) Program.loaded.Set_mem[r.name][r.i_X] = MiscFuncts.parsestr(src);
                    //create
                    else if (mem_man.isFormatOf(Inputtype.set, src)) Program.loaded.Set_mem[dest] = SetFuncts.makeSet(src);
                    else
                    {
                        if (char.IsLetter(src, 0))
                        {
                            List<double> its = new List<double>();
                            List<float> fits = new List<float>();
                            foreach (char c in src) its.Add(Convert.ToDouble((int)c));
                            switch (Program.loaded.Set_mem.StorageMode)
                            {
                                case SetStorageType.mDouble:
                                    foreach (char c in src) its.Add((int)c);
                                    Program.loaded.Set_mem[dest] = new DoubleSet(its);
                                    break;
                                case SetStorageType.mFloat:
                                    foreach (char c in src) fits.Add((int)c);
                                    Program.loaded.Set_mem[dest] = new FloatSet(fits);
                                    break;
                            }
                        }
                        else
                        {
                            double dim = MiscFuncts.parsestr(src);
                            switch (Program.loaded.Set_mem.StorageMode)
                            {
                                case SetStorageType.mDouble:
                                    Program.loaded.Set_mem[dest] = new DoubleSet(Convert.ToInt32(dim));
                                    break;
                                case SetStorageType.mFloat:
                                    Program.loaded.Set_mem[dest] = new FloatSet(Convert.ToInt32(dim));
                                    break;
                            }
                        }
                    }
                    ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mSet);
                    break;
                case 4: //string
                    //copy->
                    if (Program.loaded.String_mem.isset(src)) Program.loaded.String_mem[dest] = Program.loaded.String_mem[src];
                    else // ceate
                    {
                        Program.loaded.String_mem[dest] = src;
                    }
                    ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mString);
                    break;
            }
        }

        public static void Remove(string item)
        {
            switch (item[0])
            {
                case '$':
                    if (item.EndsWith("[]"))
                    {
                        Program.loaded.Set_mem.unset(item);
                        ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mSet);
                    }
                    else
                    {
                        Program.loaded.Mem.unset(item);
                        ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mFloat);
                    }
                    break;
                case '#':
                    Program.loaded.Mtrx_mem.unset(item);
                    ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mMatrix);
                    break;
                case '&':
                    Program.loaded.CPLX_mem.unset(item);
                    ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mCplx);
                    break;
                case '~':
                    Program.loaded.String_mem.unset(item);
                    ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mString);
                    break;
                default:
                    throw new CSException("Invalid variable type");
            }
        }

        public static void ClearAll(string segment)
        {
            if (segment.ToUpper() == "ALL")
            {
                Program.loaded.Mem.Clear();
                Program.loaded.Mtrx_mem.Clear();
                Program.loaded.CPLX_mem.Clear();
                Program.loaded.Set_mem.Clear();
                ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mAll);
            }
            else if (segment.ToUpper() == "FLOAT")
            {
                Program.loaded.Mem.Clear();
                ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mFloat);
            }
            else if (segment.ToUpper() == "MTRX")
            {
                Program.loaded.Mtrx_mem.Clear();
                ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mMatrix);
            }
            else if (segment.ToUpper() == "CPLX")
            {
                Program.loaded.CPLX_mem.Clear();
                ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mCplx);
            }
            else if (segment.ToUpper() == "STRING")
            {
                Program.loaded.String_mem.Clear();
                ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mString);
            }
            else if (segment.ToUpper() == "SET")
            {
                Program.loaded.Set_mem.Clear();
                ((Parentform)Program.loaded.Parent).UpdateMemoryMap(mMapSegment.mSet);
            }
            else throw new CSException("Invalid memory segment. Segments are: all, float, cplx, mtrx, set, string");
        }

        public static double size(string varname)
        {
            if (Program.loaded.Mem.isset(varname)) return 8; // 8byte double
            else if (Program.loaded.Mtrx_mem.isset(varname)) return 8 * Program.loaded.Mtrx_mem[varname].Columns * Program.loaded.Mtrx_mem[varname].Rows;
            else if (Program.loaded.CPLX_mem.isset(varname)) return 16; //2*8byte
            else if (Program.loaded.Set_mem.isset(varname)) return 8 * Program.loaded.Set_mem[varname].Count;
            else if (Program.loaded.String_mem.isset(varname)) return 2 * Program.loaded.String_mem[varname].Length;
            else throw new CSException("Invalid variable: " + varname);
        }
    }
}
